import sys
import serial
import serial.tools.list_ports
from PyQt5 import  QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QComboBox, QLineEdit, QFormLayout, QHBoxLayout, QVBoxLayout, QLabel, QProgressBar, QTextEdit, QWidget,QFileDialog
from ymodem import YModem
from PyQt5.QtGui import QIcon,QCursor
from PyQt5.QtCore import pyqtSignal, QObject,QTimer
import time
import threading
import platform
from utils import resource_path
from PyQt5.QtWidgets import QMessageBox
import subprocess  

#获取图片路径（打包可执行文件中的图片路径）
image_path = resource_path("img/")

class YModemUI(QMainWindow):

    BAUDRATES = {
        '115200': 115200,
        '420000': 420000,
        '921600': 921600,
    }

    def __init__(self):
        super().__init__()

        self.setWindowTitle("iap update tool v1.0")
        self.setWindowIcon(QIcon(image_path+'update.ico'))

        screen_size = QtWidgets.QDesktopWidget().screenGeometry()
        P_screen_w = int(0.25*screen_size.width())
        P_screen_h = int(0.5*screen_size.height())
        self.resize(P_screen_w, P_screen_h)
        # self.resize(400, 400)
        self.file_path = None  # 保存文件路径
        self.device_baudrate = None  # 设备波特率
        self.YModem_Status = False  # YModem升级状态
        self.update_step = 0  # YModem升级步骤
        self.port_num = 0  # 串口号
        self.initUI()
    def initUI(self):
        # 线程锁
        self.lock = threading.Lock()
        self.y_mode = YModem()
        layout = QVBoxLayout()
        form_layout = QFormLayout()

        self.port_label = QLabel('Serial port:')
        self.port_input = QComboBox()  # 使用 QComboBox 替换 QLineEdit

        # self.populate_ports()  # 填充可用串口

        self.baudrate_label = QLabel('Baudrate:')
        self.baudrate_input = QComboBox()  # 创建波特率下拉框
        self.baudrate_input.addItems(list(self.BAUDRATES.keys()))

        self.file_label = QLabel('bin file:')
        self.file_input = QLineEdit()
        self.browse_button = QPushButton('Browse')
        self.browse_button.clicked.connect(self.browse_file)

        self.refresh_button = QPushButton('Refresh')
        self.refresh_button.clicked.connect(self.populate_serial_ports)
        self.refresh_button.setFixedWidth(int(0.16*self.width()))  # 或者使用其他方法来设置大小
        # 添加发送IAP升级命令的按钮
        self.send_enter_button = QPushButton('Update')
        self.send_enter_button.clicked.connect(self.confirm_upgrade)

        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)

        # 添加用于显示接收到的数据的 QTextEdit
        self.received_data_label = QLabel('Tips:')
        self.received_data_display = QTextEdit()
        self.received_data_display.setReadOnly(True)  # 设置为只读

        port_layout = QHBoxLayout()
        port_layout.addWidget(self.port_input)
        port_layout.addWidget(self.refresh_button)
        form_layout.addRow(self.port_label, port_layout)

        layout.addLayout(form_layout)

        baudrate_layout = QHBoxLayout()
        baudrate_layout.addWidget(self.baudrate_input)
        form_layout.addRow(self.baudrate_label, baudrate_layout)

        # 添加文件选择框
        file_layout = QHBoxLayout()
        file_layout.addWidget(self.file_input)
        file_layout.addWidget(self.browse_button)

        form_layout.addRow(self.file_label, file_layout)

        layout.addWidget(self.send_enter_button)  # 确认升级
        layout.addWidget(self.progress_bar)  # 新增进度条
        layout.addWidget(self.received_data_label)
        layout.addWidget(self.received_data_display)  # 添加文本显示框

        container = QWidget()
        container.setLayout(layout)

        self.setCentralWidget(container)
        self.populate_serial_ports()
        self.y_mode.progress_signal.connect(self.update_progress) # 把YModem的进度信号连接到槽函数
        self.y_mode.txt_signal.connect(self.update_text) # 把YModem的进度信号连接到槽函数
        # 并发接收数据的定时器
        self.timer = None
        self.file_path = None  # 保存文件路径

        self.Timer = QTimer(self)
        self.Timer.timeout.connect(self.adjust_size_to_screen)
        self.Timer.start(500)  # The update window size is triggered every 500 ms

    def adjust_size_to_screen(self):
        """ 调整窗口大小 """
        screen_number = QtWidgets.QApplication.desktop().screenNumber(QCursor.pos())
        # print(f"screen number is {screen_number}")
        if screen_number == 1:
            screen = QtWidgets.QApplication.desktop().screen(screen_number)
            screen_size = screen.geometry().size()
        else:
            screen_size = QtWidgets.QDesktopWidget().screenGeometry()
        P_screen_w = int(0.25*screen_size.width())
        # P_screen_h = int(0.5*screen_size.height())
        # self.resize(P_screen_w, P_screen_h)
        self.refresh_button.setFixedWidth(int(0.16*P_screen_w))  # 或者使用其他方法来设置大小

    def get_baudrate(self):
        """ 获取选中的波特率 """
        return self.BAUDRATES.get(self.baudrate_input.currentText())

    def populate_ports(self):
        """Lists all available serial ports and returns a list of port names and device model names"""
        ports = serial.tools.list_ports.comports()
        serial_ports = []
        for port in ports:
            port_model = self.get_device_model(port) 
            if 'STM' or 'USB-SERIAL' in port_model or'usbmodem' in port.device: 
                port_model = self.get_device_model(port) 
                self.port_num = port.device
                display_name = f"{self.port_num} : {port_model}"
                serial_ports.append(display_name)
        return serial_ports

    def populate_serial_ports(self):
        """List all available serial ports and display them in comboBox_select_serial_port"""
        ports = self.populate_ports()
        self.port_input.clear()
        self.port_input.addItems(ports)

    def update_text(self, message):
        """ 更新文本框 """
        self.received_data_display.append(message)
        # QTimer.singleShot(0, lambda: self.received_data_display.append(message))
    def update_progress(self, value):
        ''' 更新进度条 '''
        self.progress_bar.setValue(value)
        # QTimer.singleShot(0, lambda: self.progress_bar.setValue(value))

    def browse_file(self):
        """ 选择文件 """
        file_name, _ = QFileDialog.getOpenFileName(self, 'Select File', '', 'All Files (*)')
        if file_name:
            self.file_input.setText(file_name)

    def confirm_upgrade(self):
        """ 确认升级 """
        self.file_path = self.file_input.text()
        self.device_baudrate = self.baudrate_input.currentText()
    
        if not self.file_path:
            self.y_mode.txt_signal.emit('Please select a bin file to send!') 
            self.serial_warning_message()
            return
        
        selected_port = self.port_input.currentText().split(" : ")[0]  # 获取选中的串口
        baudrate = self.get_baudrate()  # 获取选中的波特率

        self.y_mode.open(selected_port, baudrate)

        self.y_mode.progress_signal.emit(0)  # 设置初始进度为0%
        self.send_iap_command()  # 发送IAP升级命令

        print(f"the baudrate is {self.device_baudrate}")
        self.y_mode.txt_signal.emit(f"the device is {self.device_baudrate}")
        
        time.sleep(1)
        self.send_file()

    def send_iap_command(self): 
        """ 发送IAP升级命令 """
        try:
            hex_data = bytes([0x60, 0xF1, 0x55, 0x55])
            self.y_mode.ser.write(hex_data)

            print("The IAP upgrade command was sent ")
            self.y_mode.txt_signal.emit("The IAP upgrade command was sent")

        except Exception as e:
            print(f"send command error: {e}")
            self.y_mode.txt_signal.emit(f"send command error: {e}")
            self.serial_error_message_disconnect()
            return
            # self.y_mode.close()

    def send_enter_iap_command(self):
        """ 发送确认升级命令 """
        try:
            # with serial.Serial(port, baudrate, timeout=1) as ser:
                # 发送字符 '1'
                self.y_mode.ser.write(b'1')
                print("starting upgrade...")
                self.y_mode.txt_signal.emit("starting upgrade...") 
                self.update_step = 3
        except Exception as e:
            print(f"send enter iap command error: {e}")
            self.y_mode.txt_signal.emit(f"send enter iap command error: {e}")

    def send_file(self):
        """ 发送文件 """
        self.y_mode.progress_signal.emit(5)  # 设置进度为5%
        try:
            self.YModem_Status =self.y_mode.send(self.file_path)
            if self.YModem_Status is True:
                if self.device_baudrate == '921600':
                    self.show_success_reboot_information()
                else:
                    self.show_success_information()

        finally:
                self.y_mode.close()
    def read_serial_response(self,ser, expected_response, timeout=1):
        """读取串口数据直到得到预期回复或超时。"""
        start_time = time.time()
        response = ""

        while True:
            if ( time.time() - start_time ) > timeout:
                print("读取超时！")
                return None  # 超时处理
            
            if ser.in_waiting > 0:
                response = ser.readline().decode().strip()
                print(response)  # 打印串口收到的数据
                self.y_mode.txt_signal.emit(f'{response}') 
                if response == expected_response:
                    return response  # 返回预期的回复

    def get_device_model(self, port):
        """Obtain the device model based on the operating system"""
        os_name = platform.system()
        if os_name == "Windows":
            return port.description
        else:
            return "Unknown"               



    
    def serial_warning_message(self):
        """弹出选择文件警告框"""
        QMessageBox.warning(self,"Warning","Please select a bin file to send!",QMessageBox.Yes | QMessageBox.Yes)

    def serial_error_message_rebot(self):
        """弹出警告框"""
        QMessageBox.critical(self,"Radio Error","Please Re-Boot your radio and try again!",QMessageBox.Yes | QMessageBox.Yes)

    def serial_error_message_disconnect(self):
        """弹出警告框"""
        QMessageBox.critical(self,"Connect Error","Please disconnect and try again!",QMessageBox.Yes | QMessageBox.Yes)

    def show_success_information(self):
        """弹出成功信息框"""
        QMessageBox.information(self ,"information", "File sent successfully!",QMessageBox.Yes | QMessageBox.Yes)

    def show_success_reboot_information(self):
        """弹出成功并重启信息框"""
        QMessageBox.information(self ,"information", "File sent successfully! \nPlease Re-Boot your radio!",QMessageBox.Yes | QMessageBox.Yes)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = YModemUI()
    main_window.show()
    sys.exit(app.exec_())
